use std::sync::Arc;
use std::time::Duration;

use collab::core::any_map::AnyMapExtension;
use collab_database::database::gen_row_id;
use collab_database::rows::{new_cell_builder, CreateRowParams, RowChange};
use collab_database::views::DatabaseViewChange;
use tokio::time::sleep;

use crate::database_test::helper::{create_database, wait_for_specific_event};

#[tokio::test]
async fn observer_create_new_row_test() {
  let database_id = uuid::Uuid::new_v4().to_string();
  let database_test = Arc::new(create_database(1, &database_id).await);
  let view_change_rx = database_test.subscribe_view_change();

  let row_id = gen_row_id();
  let cloned_row_id = row_id.clone();
  let cloned_database_test = database_test.clone();
  tokio::spawn(async move {
    sleep(Duration::from_millis(300)).await;
    let row = CreateRowParams::new(cloned_row_id, database_id.clone());
    cloned_database_test.create_row(row).unwrap();
  });

  wait_for_specific_event(view_change_rx, |event| match event {
    DatabaseViewChange::DidInsertRowOrders { row_orders } => {
      row_orders.len() == 1 && row_orders[0].id == row_id
    },
    _ => false,
  })
  .await
  .unwrap();
}

#[tokio::test]
async fn observer_row_cell_test() {
  let database_id = uuid::Uuid::new_v4().to_string();
  let database_test = Arc::new(create_database(1, &database_id).await);
  let row_change_rx = database_test.subscribe_row_change();
  let row_id = gen_row_id();

  // Insert cell
  let cloned_row_id = row_id.clone();
  let cloned_database_test = database_test.clone();
  tokio::spawn(async move {
    sleep(Duration::from_millis(300)).await;
    cloned_database_test
      .create_row(CreateRowParams::new(
        cloned_row_id.clone(),
        database_id.clone(),
      ))
      .unwrap();

    cloned_database_test.update_row(&cloned_row_id, |row| {
      row.update_cells(|cells| {
        cells.insert_cell(
          "f1",
          new_cell_builder(1).insert_i64_value("level", 1).build(),
        );
      });
    });
  });

  wait_for_specific_event(row_change_rx, |event| match event {
    RowChange::DidUpdateCell {
      row_id: _,
      field_id,
      value,
    } => field_id == "f1" && value.get_i64_value("level") == Some(1),
    _ => false,
  })
  .await
  .unwrap();

  // Update cell
  let cloned_database_test = database_test.clone();
  let row_change_rx = database_test.subscribe_row_change();
  tokio::spawn(async move {
    sleep(Duration::from_millis(300)).await;

    cloned_database_test.update_row(&row_id, |row| {
      row.update_cells(|cells| {
        cells.insert_cell(
          "f1",
          new_cell_builder(1).insert_i64_value("level", 2).build(),
        );
      });
    });
  });

  wait_for_specific_event(row_change_rx, |event| match event {
    RowChange::DidUpdateCell {
      row_id: _,
      field_id,
      value,
    } => field_id == "f1" && value.get_i64_value("level") == Some(2),
    _ => false,
  })
  .await
  .unwrap();
}

#[tokio::test]
async fn observer_update_row_test() {
  let database_id = uuid::Uuid::new_v4().to_string();
  let database_test = Arc::new(create_database(1, &database_id).await);
  let row_change_rx = database_test.subscribe_row_change();

  let row_id = gen_row_id();
  let cloned_database_test = database_test.clone();
  tokio::spawn(async move {
    sleep(Duration::from_millis(300)).await;
    cloned_database_test
      .create_row(CreateRowParams::new(row_id.clone(), database_id.clone()))
      .unwrap();

    cloned_database_test.update_row(&row_id, |row| {
      row.set_height(1000);
    });
  });

  wait_for_specific_event(row_change_rx, |event| match event {
    RowChange::DidUpdateHeight { row_id: _, value } => *value == 1000i32,
    _ => false,
  })
  .await
  .unwrap();
}
